using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.IO;
using SlimDX;
using SlimDX.Direct3D9;

namespace TileGert
{
  /// <summary>
  /// This is the main class of GERT. To create a game inherit from this class
  /// and override the BeforeInit, AfterInit, Update or Render methods.
  /// To start the game call the Run method.
  /// </summary>
  public class GertGame
  {
    //Gert specific
    private GertContent _content = null;
    private GertGameState _gameState = GertGameState.Invalid;
    private GertLogger _logger = null;
    private GertViewport _viewport = null;
    private GertRenderer _renderer = null;
    private GertInput _input = null;

    /// <summary>
    /// Creates a new game. No computing is done in here. To run the game call the Run method.
    /// </summary>
    public GertGame()
    {
    }

    [DllImport("Kernel32.dll")]
    private static extern bool QueryPerformanceCounter(out long lpPerformanceCount);

    [DllImport("Kernel32.dll")]
    private static extern bool QueryPerformanceFrequency(out long lpFrequency);

    /// <summary>
    /// Gets the current state of the game.
    /// </summary>
    public GertGameState GameState
    {
      get { return _gameState; }
    }

    /// <summary>
    /// Gets or sets the the renderer used to display the game.
    /// </summary>
    public GertRenderer Renderer
    {
      get { return _renderer; }
      set { _renderer = value; }
    }

    /// <summary>
    /// Gets the content object that can be used to load content like textures, sound etc.
    /// </summary>
    public GertContent Content
    {
      get { return _content; }
    }

    /// <summary>
    /// Gets or sets the logger that is used for logging. By default the log is written to the file
    /// "gert.log" in the executable path.
    /// </summary>
    public GertLogger Logger
    {
      get { return _logger; }
      set { _logger = value; }
    }

    /// <summary>
    /// Gets or sets the the current viewport. This is the area that is visible on the screen.
    /// </summary>
    public GertViewport Viewport
    {
      get { return _viewport; }
      set { _viewport = value; }
    }
    
    /// <summary>
    /// Gets or sets the input handler associated with this game.
    /// </summary>
    public GertInput Input
    {
      get { return _input; }
      set { _input = value; }
    }

    /// <summary>
    /// Use this method to run the game.
    /// </summary>
    public void Run()
    {
      if (_gameState != GertGameState.Invalid)
        throw new Exception("The StartGame method has already been called! It can only be called once.");

      if (DoInit())
      {
        long tick1, tick2, freq;
        QueryPerformanceFrequency(out freq);
        //Initialize ticks so there is no timestep at first frame.
        QueryPerformanceCounter(out tick1);
        tick2 = tick1;

        _renderer.CanvasVisible = true;

        while (_renderer.CanvasCreated)
        {
          //Calculate time since last frame
          QueryPerformanceCounter(out tick1);
          float time = (tick1 - tick2) / (float)freq;
          QueryPerformanceCounter(out tick2);
          DoUpdate(time);

          DoRender();

          Application.DoEvents();
        }
      }
    }

    /// <summary>
    /// Initializes the game.
    /// </summary>
    private bool DoInit()
    {
      _gameState = GertGameState.Initializing;      
      _logger = new GertLogger(Path.GetDirectoryName(Application.ExecutablePath) + "\\gert.log");
      _renderer = new GertRenderer(this);
      _input = new GertInput(this);

      BeforeInit();

      if (_renderer.Initialize())
      {
        _content = new GertContent(this); //Content needs the renderer to be initialzed!!
        _viewport = new GertViewport(0, 0, _renderer.ScreenWidth, _renderer.ScreenHeight);
        AfterInit();
        return true;
      }
      else
        return false;
    }

    /// <summary>
    /// Override this method to be able to do processing before the initialization starts.
    /// Things like the screen resolution have to be set before initialization.
    /// </summary>
    protected virtual void BeforeInit()
    {
    }

    /// <summary>
    /// Override this method to be able to do processing after the initialization is done.
    /// </summary>
    protected virtual void AfterInit()
    {
    }

    /// <summary>
    /// Calls the update method.
    /// </summary>
    /// <param name="timestep">The time that apleased since the last update in seconds.</param>
    private void DoUpdate(float timestep)
    {
      Update(timestep);
    }

    /// <summary>
    /// Override this method to be able to do processing before each frame.
    /// Using the timestep parameter you can advance your animations and simulations
    /// by the time apleased since the last frame.
    /// </summary>
    /// <param name="timestep">The time that apleased since the last update in seconds.</param>
    protected virtual void Update(float timestep)
    {
    }

    /// <summary>
    /// Calls the Render method.
    /// </summary>
    private void DoRender()
    {
      Render();
    }

    /// <summary>
    /// Override this method to be able to include your own rendering code.
    /// Use the Renderer property to draw graphics on the screen.
    /// </summary>
    protected virtual void Render()
    {
      //DONE: Rendering in eigener Klasse abstrahieren
      _renderer.Clear(GertColor.Blue);
      _renderer.Begin();

      int x = _renderer.ScreenWidth / 2 - 128;
      int y = _renderer.ScreenHeight / 2 - 64;
      _renderer.DrawSprite("tilegert_logo", x, y);

      _renderer.End();

      /*_d3dDevice.Clear(ClearFlags.Target, System.Drawing.Color.Blue, 1.0f, 1);
      _d3dDevice.BeginScene();

      _sprite.Begin(SpriteFlags.AlphaBlend);
      Vector3 pos = new Vector3((float)((_screenWidth / 2) - 128), (float)((_screenHeight / 2) - 64), 0f);
      _sprite.Draw(_content.GetAsset<Texture>("tilegert_logo"), Vector3.Zero, pos, System.Drawing.Color.White);
      _sprite.End();

      _d3dDevice.EndScene();
      _d3dDevice.Present();*/
    }

  }
}
